package com.example.venue_reservation_service.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.venue_reservation_service.domain.*;
import com.example.venue_reservation_service.dto.DelDTO;
import com.example.venue_reservation_service.dto.RAddDTO;
import com.example.venue_reservation_service.dto.ReservationDTO;
import com.example.venue_reservation_service.dto.SignInDTO;
import com.example.venue_reservation_service.entity.RecommendVO;
import com.example.venue_reservation_service.mapper.*;
import com.example.venue_reservation_service.mq.DelayMessageSender;
import com.example.venue_reservation_service.service.*;
import com.example.venue_reservation_service.utils.DateUtil;
import com.example.venue_reservation_service.utils.DownExcel;
import com.example.venue_reservation_service.utils.ScheduleUtil;
import com.example.venue_reservation_service.utils.VenueRecommender;
import com.example.venue_reservation_service.vo.*;
import com.example.venue_reservation_service.vo.excel.PaymentExportDTO;
import com.example.venue_reservation_service.vo.excel.ReservationExcel;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author 31586
* @description 针对表【venue_reservation】的数据库操作Service实现
* @createDate 2024-11-13 11:01:43
*/
@Service
public class ReservationServiceImpl extends ServiceImpl<ReservationMapper, Reservation>
    implements ReservationService{

    @Resource
    private BalanceService balanceService;

    @Resource
    private PaymentService paymentService;

    @Resource
    private InformationMapper informationMapper;


    @Resource
    private TypeMapper typeMapper;

    @Resource
    private AdminMapper adminMapper;

    @Resource
    private InformationService informationService;

    @Resource
    private UserService userService;

    @Resource
    private ScheduleUtil scheduleUtil ;

    @Resource
    private SlotMapper slotMapper;

    @Resource
    private VenueSlotService venueSlotService;

    @Resource
    private DelayMessageSender delayMessageSender;

    @Resource
    private ReservationMapper reservationMapper;

    @Resource
    private PriceService priceService;

    @Transactional
    @Override
    public Result addReservation(RAddDTO dto) {

        // 校验日期是否合适
        LocalDate reservationDate = dto.getReservationDate();
        if(reservationDate.isBefore(LocalDate.now())){
            // 如果预约日期早于当前日期
            return Result.fail().message("预约日期需在"+LocalDate.now()+"及之后");
        }

        // 判断场地是否存在
        Information information = informationService.getById(dto.getVenueId());

        if (Optional.ofNullable(information).isEmpty()) {
            return Result.fail().message("场地数据不存在");
        }

        // 判断用户是否有待签到的场地预约信息
        if (Optional.ofNullable(getOne(Wrappers.<Reservation>lambdaQuery()
                        .eq(Reservation::getUserId, dto.getUserId())
                        .eq(Reservation::getAuditStatus, 1)
                        .eq(Reservation::getReservationDate, reservationDate)))
                .isPresent()) {
            return Result.fail().message( reservationDate +"存在待签到的订单");
        }

        // 获取此次预约的时间段
        List<TimeItem> times = dto.getTimes();
        if (times.size() == 0) {
            return Result.fail().message("预约时间段不能为空");
        }

        // 使用 Stream API 进行排序
        List<TimeItem> sortedTimeItems = times.stream()
                .sorted(Comparator.comparingInt(TimeItem::getId)).toList();
        // 取出预约时间的开始和结束时间
        LocalTime start = sortedTimeItems.get(0).getStartTime();
        LocalTime end = sortedTimeItems.get(sortedTimeItems.size() - 1).getEndTime();

        // 如果是今天进行预约
        if(reservationDate.compareTo(LocalDate.now()) == 0){
            // 校验事件
            if (LocalTime.now().isAfter(end)){
                return Result.fail().message("无效预约时间");
            }
        }

        // 检查当前时间段内场地是否被禁用
        List<Slot> slots = slotMapper.selectList(Wrappers.<Slot>lambdaQuery()
                .eq(Slot::getReservationDate, reservationDate)
                .and(wrap -> wrap
                        .lt(Slot::getSlotStart, end)   // 条件1：slot_start < end
                        .gt(Slot::getSlotEnd, start)   // 条件2：slot_end > start
                )
        );
        for (Slot slot : slots) {
            List<VenueSlot> list = venueSlotService.list(Wrappers.<VenueSlot>lambdaQuery()
                    .eq(VenueSlot::getVenueId, information.getId())
                    .eq(VenueSlot::getSlotId, slot.getId()));
            if(list.size() > 0){
                // 存在禁用时间段，不可预约
                return Result.fail().message("预约时间段内存在禁用时间段");
            }
        }

        // 判断该预约时间是否合理（即查询用户预约的日期下，该场地的此时间段内是否存在其他的预约订单）
        long count = count(Wrappers.<Reservation>lambdaQuery()
                .eq(Reservation::getReservationDate, reservationDate)
                .eq(Reservation::getVenueId, dto.getVenueId())
                .in(Reservation::getAuditStatus, 1, 2)
                .and(wrap -> wrap
                        .lt(Reservation::getStartTime, end)    // 简化的重叠条件
                        .gt(Reservation::getEndTime, start)
                )
        );
        if (count>0) {
            // 存在其他订单数据，不可预约
            return Result.fail().message("预约时间段内存在其他用户订单");
        }

        // 检查用户账号数据
        // 获取用户账号余额
        Balance balance = balanceService.getOne(Wrappers.<Balance>lambdaQuery()
                .eq(Balance::getUserId, dto.getUserId()));
        Double aDouble = balance.getBalance();

        // 获取场地的使用费用
        Double money = dto.getMoney();
        // 判断用户余额是否可以完成此次支付
        if(aDouble < money){
            return Result.fail().message("账户余额不足，请进行充值");
        }

        // 修改账户余额
        aDouble -= money;
        balance.setBalance(aDouble);
        // 生成支付记录
        Payment payment = new Payment(dto.getUserId(), money, "预约支出", LocalDateTime.now());
        paymentService.save(payment);

        balanceService.updateById(balance);

        // 添加预约信息
        Reservation reservation = BeanUtil.copyProperties(dto, Reservation.class);
        // 将预约状态设置为 1 即待签到
        reservation.setAuditStatus(1);
        // 设置当前预约对应的支付信息
        reservation.setPayId(payment.getId());
        // 设置预约开始和结束时间
        reservation.setStartTime(start);
        reservation.setEndTime(end);
        reservation.setCreateTime(LocalDateTime.now());
        reservation.setIsDelete(0);
        // 保存预约信息
        save(reservation);

        // 设置定时任务（即到预约结束时间进行检查签到情况）
        // 任务
//        Runnable task = () -> {
//            // 处理当前预约
//            dealReservation(reservation.getId());
//        };

//        scheduleUtil.scheduleOneTimeTask(task, reservationDate, end);
//        // 定时任务解耦设计
//        scheduleUtil.scheduleOneTimeTask(
//                () -> dealReservation(reservation.getId()),
//                reservationDate,
//                end
//        );

        // 发送延迟消息到 RabbitMQ
        delayMessageSender.sendReservationCheck(reservation.getId(), reservationDate, end);

        // 返回订单信息
        return Result.ok(payment).message("预约成功");
    }

    @Override
    @Async
    public void dealReservation(Integer reservationId) {
        Reservation reservation = getById(reservationId);
        // 判空操作
        if (Optional.ofNullable(reservation).isPresent()) {
            Integer status = reservation.getAuditStatus();
            // 未签到：将该条预约数据的状态设置为 -2，同时归还支付金额
            if(status == 1){
                reservation.setAuditStatus(-2);
                updateById(reservation);
                // 修改交易记录
                Payment payment = paymentService.getById(reservation.getPayId());
                payment.setSituation("过期");
                // 归还支付金额
                balanceService.returnMoney(reservation.getUserId(), payment.getAmount());
                payment.setAmount(0.0);
                paymentService.updateById(payment);

            }
        }
    }

    @Override
    public Result getReservations(ReservationDTO dto) {
        Page<Reservation> page = new Page<>(dto.getCurrent(), dto.getSize());
        LambdaQueryWrapper<Reservation> wrapper = new LambdaQueryWrapper<>();

        Integer status = dto.getStatus();
        if(status < -2 || status > 5){
            return Result.fail().message("非法预约状态");
        }

        System.out.println(dto);
        if (status != 0) {
            wrapper.eq(Reservation::getAuditStatus, status);
        }

        Integer userId = dto.getUserId();
        Integer userType = userService.getById(userId).getType();
        if (userType != 0) {
            // 判断是否为管理员
            if(userType == 3) {
                // 为管理员，获取管理的场馆信息
                Integer typeId = adminMapper.selectOne(Wrappers.<Admin>lambdaQuery().eq(Admin::getUserId, userId)).getTypeId();
                // 获取场馆下场地列表
                List<Integer> venueIds = informationService.getIdsByTypeId(typeId);
                wrapper.in(Reservation::getVenueId, venueIds);
            } else {
                // 用户
                wrapper.eq(Reservation::getUserId, userId);
            }
        }else {
            wrapper.eq(Reservation::getUserId, userId);
        }

        wrapper.eq(Reservation::getIsDelete, 0).orderByDesc(Reservation::getReservationDate).orderByDesc(Reservation::getCreateTime);
        page = page(page, wrapper);


        List<ReservationVO> list = new ArrayList<>();
        for (Reservation reservation : page.getRecords()) {
            // 获取场地名称
            Information information = informationMapper.selectOne(Wrappers
                    .<Information>lambdaQuery()
                    .eq(Information::getId, reservation.getVenueId()));

            // 获取对应场馆名称
            Type type = typeMapper.selectOne(Wrappers
                    .<Type>lambdaQuery()
                    .eq(Type::getId, information.getTypeId()));
            // 预约时间段
            String timeStr = reservation.getStartTime() + "~" + reservation.getEndTime();

            // 获取支付信息
            Payment payment = paymentService.getById(reservation.getPayId());

            ReservationVO result = new ReservationVO(
                    reservation.getId(),
                    information.getId(),
                    information.getType(),
                    information.getVenueName(),
                    reservation.getAuditStatus(),
                    timeStr,
                    reservation.getReservationDate() + "",
                    type.getVname(),
                    payment.getId(),
                    DateUtil.getEpochMilli(payment.getTime())+ "-" + payment.getId(),
                    payment.getAmount(),
                    payment.getTime()
            );
            list.add(result);
        }
        QueryVO<ReservationVO> vo = new QueryVO<>(list, page.getTotal());
        return Result.ok(vo).message("查询成功");
    }

    @Override
    public Result cancelReservation(Integer reservationId) {
        Reservation reservation = getById(reservationId);

        if (Optional.ofNullable(reservation).isEmpty()) {
            return Result.fail().message("无订单信息");
        }

        if (reservation.getAuditStatus() !=1) {
            return Result.fail().message("仅当订单待签到才可以进行取消");
        }

        if (reservation.getReservationDate().compareTo(LocalDate.now()) == 0) {
            // 获取订单日期
            if (LocalTime.now().isAfter(reservation.getStartTime()) && LocalTime.now().isBefore(reservation.getStartTime())) {
                // 对于当天使用中的订单
                // 如果是未签到，可以进行取消（后期如果引入信用机制扣除荣誉分）
                Integer status = reservation.getAuditStatus();
                if (status == 2) {
                    return Result.fail().message("订单已签到");
                }
            }
        }

        // 将预约订单状态设置为 -1，即表示取消订单
        reservation.setAuditStatus(-1);
        updateById(reservation);
        // 归还支付金额
        Payment payment = paymentService.getById(reservation.getPayId());
        // 修改支付情况
        payment.setAmount(0.0);
        payment.setSituation("取消");
        payment.setTime(LocalDateTime.now());
        paymentService.updateById(payment);
        // 修改用户账户余额
        balanceService.returnMoney(reservation.getUserId(), payment.getAmount());
        return Result.ok().message("预约订单取消成功");
    }

    @Override
    public Result signInReservation(SignInDTO dto) {
        Reservation reservation = getById(dto.getReservationId());

        if (Optional.ofNullable(reservation).isEmpty()) {
            return Result.fail().message("预约信息不存在");
        }

        if(!Objects.equals(reservation.getUserId(), dto.getUserId())){
            return Result.fail().message("非本人订单");
        }

        if (!Objects.equals(reservation.getVenueId(), dto.getVenueId())) {
            return Result.fail().message("非该场地二维码，请更换正确的");
        }

        if (reservation.getAuditStatus() != 1) {
            return Result.fail().message("预约信息不为待签到状态");
        }

        // 签到时间可以在预约开始时间提前 15 分钟 或者 在预约时间段内
        // 获取预约开始时间段
        LocalTime startTime = reservation.getStartTime();

        if (reservation.getReservationDate().compareTo(LocalDate.now()) != 0) {
            return Result.fail().message("非当天订单");
        }
        // 判断当前时间是否合理
        if (reservation.getEndTime().isBefore(LocalTime.now())){
            return Result.fail().message("订单已经过期");
        }

        // 提前签到
        if (startTime.minusMinutes(15).isAfter(LocalTime.now())) {
            return Result.fail().message("无效签到时间");
        }

        // 正常时间段签到
        // 将当前签到订单设置
        reservation.setAuditStatus(2);
        reservation.setCheckInTime(LocalDateTime.now());
        updateById(reservation);

        // 返回预约信息
        return Result.ok(reservation).message("订单签到成功");
    }

    @Override
    public Result queryDetail(Integer id) {
        Reservation reservation = getById(id);
        if (Optional.ofNullable(reservation).isEmpty()) {
            return Result.fail().message("未查询到预约信息");
        }

        // 加载场地信息
        Information information = informationService.getById(reservation.getVenueId());

        // 加载支付信息
        Payment payment = paymentService.getById(reservation.getPayId());

        ReservationDetail detail = new ReservationDetail(
                information.getVenueName(),
                DateUtil.getEpochMilli(payment.getTime()) + id,
                reservation.getReservationDate(),
                reservation.getStartTime() + "~" + reservation.getEndTime(),
                payment.getAmount(),
                payment.getTime()
        );

        return Result.ok(detail).message("ok");
    }

    @Override
    public Result exportData(String time) {
        LocalDateTime startTime = null;
        LocalDateTime endTime = LocalDateTime.now(); // 查询截止到当前时间

        // 根据时间范围计算起始时间
        switch (time) {
            case "今年" -> startTime = LocalDateTime.of(LocalDateTime.now().getYear(), 1, 1, 0, 0, 0);
            case "本月" -> {
                YearMonth currentMonth = YearMonth.now();
                startTime = LocalDateTime.of(currentMonth.getYear(), currentMonth.getMonthValue(), 1, 0, 0, 0);
            }
            case "近三月" -> startTime = LocalDateTime.now().minus(3, ChronoUnit.MONTHS);
        }
        // 构建查询条件
        LambdaQueryWrapper<Reservation> queryWrapper = new LambdaQueryWrapper<>();
        if (startTime != null) {
            queryWrapper.ge(Reservation::getCreateTime, startTime)
                    .le(Reservation::getCreateTime, endTime);
        }
        // 附加逻辑删除条件（Mybatis-plus默认会处理，但显式添加更清晰）
//        queryWrapper.eq(Reservation::getIsDelete, 0);

        return Result.ok(this.list(queryWrapper)).message("查询成功");
    }

    @Override
    public void exportExcel(HttpServletResponse response, Integer userId) {
        ReservationMapper mapper = getBaseMapper();
        try {
            DownExcel.download(response, ReservationExcel.class, mapper.selectByUserId(userId), "reservation");
        } catch (IOException | IllegalAccessException | InstantiationException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Result removeBatch(DelDTO dto) {
        List<Integer> list = dto.getList();
        if(Optional.ofNullable(list).isEmpty() || list.size() == 0){
            return Result.fail().message("请选择需要清理的数据");
        }
        int notDeleted = 0;
        for (Integer id : list) {
            Reservation reservation = getById(id);
            if(reservation.getAuditStatus() == 1 || !Objects.equals(reservation.getUserId(), dto.getUserId()) || (reservation.getAuditStatus() == 2 && LocalDateTime.now().toLocalTime().isBefore(reservation.getEndTime()))){
                notDeleted++;
                continue;
            }
            reservation.setIsDelete(1);
            updateById(reservation);
        }
        return Result.ok().message(notDeleted == 0 ? "清理成功" : "移除订单中，存在不可删除订单，已为您自动过滤");
    }

    @Override
    public Result removeSingle(Integer userId, Integer id) {
        Reservation reservation = getById(id);
        if (Optional.ofNullable(reservation).isEmpty()) {
            return Result.fail().message("订单信息不存在");
        }
        if (reservation.getAuditStatus() == 1) {
            return Result.fail().message("订单处于待预约状态，不可删除");
        }
        if (reservation.getAuditStatus() == 2  && LocalDateTime.now().toLocalTime().isBefore(reservation.getEndTime())) {
            return Result.fail().message("订单处于正在使用中状态，不可删除");
        }
        if (!Objects.equals(reservation.getUserId(), userId)) {
            return Result.fail().message("非非本人订单");
        }
        reservation.setIsDelete(1);
        updateById(reservation);
        return Result.ok().message("订单删除成功");
    }

    @Override
    public void exportPayments(HttpServletResponse response, String yearFilter) {
        try {
            // 确定要查询的年份
            Year targetYear = parseYearFilter(yearFilter);

            // 获取该年份所有账单
            List<Payment> payments = getPaymentsForYear(targetYear);
            List<PaymentExportDTO> dtos = payments.stream()
                    .map(PaymentExportDTO::fromEntity)
                    .collect(Collectors.toList());

            // 设置响应头
            String fileName = URLEncoder.encode(
                    String.format("交易账单_%s年数据", targetYear), StandardCharsets.UTF_8);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
            response.setCharacterEncoding("UTF-8");

            // 导出Excel
            EasyExcel.write(response.getOutputStream(), PaymentExportDTO.class)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .sheet("交易账单明细")
                    .doWrite(dtos);

        } catch (IOException e) {
            throw new RuntimeException("导出账单失败", e);
        }
    }

    /**
     * 解析年份过滤参数
     * @param yearFilter 年份参数（current-今年, last-去年）
     * @return 要查询的目标年份
     */
    private Year parseYearFilter(String yearFilter) {
        if ("last".equalsIgnoreCase(yearFilter)) {
            return Year.now().minusYears(1);
        }
        // 默认返回今年
        return Year.now();
    }

    /**
     * 获取指定年份的所有支付记录
     * @param year 目标年份
     * @return 该年份的支付记录列表
     */
    private List<Payment> getPaymentsForYear(Year year) {
        // 计算该年份的开始和结束时间
        LocalDateTime startOfYear = LocalDate.of(year.getValue(), 1, 1).atStartOfDay();
        LocalDateTime endOfYear = LocalDate.of(year.getValue(), 12, 31).atTime(23, 59, 59);

        return paymentService.list(Wrappers.<Payment>lambdaQuery()
                        .in(Payment::getSituation, "预约花费", "账户余额充值")
                .between(Payment::getTime, startOfYear, endOfYear));
    }


    @Override
    public Result recommend(Integer userId) {
        long count = count(Wrappers.<Reservation>lambdaQuery().eq(Reservation::getUserId, userId));
        VenueRecommender recommender = new VenueRecommender(reservationMapper.selectByUserId(count > 50 ? userId:null), userId);
        RecommendVO vo = new RecommendVO();
        ReservationExcel excel = recommender.generateRecommendation();
        int hours = excel.getEndTime().getHour() - excel.getStartTime().getHour();
        vo.setExcel(excel);
        vo.setInformation(informationService.getById(excel.getVenueId()));
        vo.setPrice(priceService.getPrice(excel.getVenueId()) * hours);
        return Result.ok(vo).message("推荐结果");
    }

}




